/******************************************************************************
 Copyright (c) 2021-2024 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

TODOs
1. file_list.txt should be updated
2. update x2p files
******************************************************************************/
#include "csr_bt_handover_if.h"
#include "csr_bt_hidd_main.h"
#include "csr_bt_marshal_util.h"
#include "csr_streams.h"
#include "csr_bt_tasks.h"
#include "csr_bt_cm_private_lib.h"
#include "csr_bt_hidd_prim.h"
#include "csr_bt_panic.h"

#define PanicIfFalse(cond) if(!(cond)) CsrPanic(CSR_TECH_BT, CSR_BT_PANIC_MYSTERY, #cond " Failed!!")
/*
 * HIDD state analysis for handover.
 * ---------------------------------
 * HIDD_INIT_STATE
 *      Ignore. Boot state.
 * HIDD_IDLE_STATE - Post Synergy Init state. Ready for activation.
 *      No need to marshal.
 * HIDD_REGISTERING_SDP - Processing Activate Request from app. Registering SDP.
 *      Veto. Short lived intermittent state.
 * HIDD_CONNECTING_STATE - Connecting to host.
 *      Veto. Short lived intermittent state.
 * HIDD_UNREGISTERING_SD - State not getting used.
 *      Veto. (future use)
 * HIDD_CONNECTED_STATE - connected to hid host.
 *      Marshal - Stream handles, remote info
 * HIDD_ACCEPT_CONNECTING_STATE - Waiting for connection.
 *      No need to marshal.
 * HIDD_NOT_CONNECTED_STATE - disconnected.
 *      No need to marshal.
 *
 * SDP Record is too big for marshalling. It has to be stored locally in profile as constant.
 *
 */

static CsrBtMarshalUtilInst *hiddConvInst;

static void HiddCleanUpAfterHandoverFailure(HiddInstanceDataType *instData)
{
    HiddStatesType savedState;
    CsrBtBdAddrZero(&(instData->bdAddr));
    HiddResetControlAndInterruptChannelData(instData, FALSE);
    /* Save state info */
    savedState = instData->state;
    HiddResetGenericInstanceData(instData);
    /* Restore state info */
    instData->state = savedState;
}

static void convHiddMainInstanceData(CsrBtMarshalUtilInst *conv, HiddInstanceDataType *hiddMainInst)
{
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->hiddCtrlChConnected);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->ctrlCh.hostMtu);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->ctrlCh.localMtu);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->ctrlCh.cid);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->intrCh.hostMtu);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->intrCh.localMtu);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->intrCh.cid);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->prevError);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->prevErrorSupplier);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->disconnectReason);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->active);
    CsrBtMarshalUtilConvertObj(conv, hiddMainInst->appRequestedDisconnect);
}

static void serHiddMainInstanceData(CsrBtMarshalUtilInst *conv,
                                    HiddInstanceDataType *hiddMainInst,
                                    const CsrBtDeviceAddr *addr)
{
    CSR_UNUSED(addr); //only one instance is supported
    convHiddMainInstanceData(conv, hiddMainInst);
}

static void deserHiddMainInstanceData(CsrBtMarshalUtilInst *conv,
                                      HiddInstanceDataType *hiddMainInst,
                                      const CsrBtDeviceAddr *addr)
{
    hiddMainInst->bdAddr = *addr;
    convHiddMainInstanceData(conv, hiddMainInst);
}


static void registerStreams(CsrBtConnId connId, CsrSchedQid qId)
{
    CsrStreamsRegister(CM_GET_UINT16ID_FROM_BTCONN_ID(connId),
                       L2CAP_ID,
                       qId);

    CsrStreamsSourceHandoverPolicyConfigure(CM_GET_UINT16ID_FROM_BTCONN_ID(connId),
                                            L2CAP_ID,
                                            SOURCE_HANDOVER_ALLOW_WITHOUT_DATA);
}

static HiddInstanceDataType* hiddGetInstanceFromBdAddr(const BD_ADDR_T *pBdAddr)
{
    HiddInstanceDataType *pHiddInstance = NULL;

#ifndef INSTALL_HIDD_MULTI_INSTANCE_SUPPORT
    {
        if (CsrBtBdAddrEq(&(csrBtHiddInstData.bdAddr), pBdAddr))
        {
            pHiddInstance = &csrBtHiddInstData;
        }
    }
#else
    {
        CsrUint8 instIndex;
        for (instIndex =0 ; instIndex < HIDD_MAX_NUM_INSTANCES ; instIndex++)
        {
            if (CsrBtBdAddrEq(&(csrBtHiddInstData[instIndex].bdAddr), pBdAddr))
            {
                pHiddInstance = &csrBtHiddInstData[instIndex];
                break;
            }
        }
    }
#endif

    return (pHiddInstance);
}

static HiddInstanceDataType* hiddGetInstanceFromQueueId(CsrSchedQid queueId)
{
    HiddInstanceDataType *pHiddInstance = NULL;

#ifndef INSTALL_HIDD_MULTI_INSTANCE_SUPPORT
    {
        if (csrBtHiddInstData.myAppHandle == queueId)
        {
            pHiddInstance = &csrBtHiddInstData;
        }
    }
#else
    {
        CsrUint8 instIndex;
        for (instIndex =0 ; instIndex < HIDD_MAX_NUM_INSTANCES ; instIndex++)
        {
            if (csrBtHiddInstData[instIndex].myAppHandle == queueId)
            {
                pHiddInstance = &csrBtHiddInstData[instIndex];
                break;
            }
        }
    }
#endif

    return (pHiddInstance);
}

static bool csrBtHiddVeto (void)
{   /* Do Nothing, Transition state veto condition is checked in csrBtHiddVetoLink(..) */
    return (FALSE);
}

static bool csrBtHiddVetoLink(const tp_bdaddr *vmTpAddrt)
{
    HiddInstanceDataType *pHiddInstance = NULL;
    CsrBtTpdAddrT currentLinkAddr;
    bool veto = FALSE;
 
    BdaddrConvertTpVmToBluestack(&currentLinkAddr, vmTpAddrt);
    pHiddInstance = hiddGetInstanceFromBdAddr(&currentLinkAddr.addrt.addr);
 
    if (pHiddInstance)
    {
        if (pHiddInstance->state == HIDD_CONNECTING_STATE ||
            pHiddInstance->state == HIDD_REGISTERING_SDP  ||
            pHiddInstance->state == HIDD_UNREGISTERING_SDP ||
            pHiddInstance->hiddPendingConnReqMask != HIDD_PENDING_MASK_NONE)
        {
            veto = TRUE;
            CSR_LOG_TEXT_INFO((CsrBtHiddLto, 0, "csrBtHiddVeto, pHiddInstance->state = 0x%02X", pHiddInstance->state));
        }
    }
    return veto;
}

static bool csrBtHiddMarshal(const tp_bdaddr *vmTpAddrt,
                             CsrUint8 *buf,
                             CsrUint16 length,
                             CsrUint16 *written)
{
    HiddInstanceDataType *pHiddInstance = NULL;
    CsrBtTpdAddrT tpAddrt = { 0 };
 
    CSR_LOG_TEXT_INFO((CsrBtHiddLto, 0, "csrBtHiddMarshal"));
    BdaddrConvertTpVmToBluestack(&tpAddrt, vmTpAddrt);
    pHiddInstance = hiddGetInstanceFromBdAddr(&tpAddrt.addrt.addr);
    /* Marshal if HID control channel is connected */
    if (pHiddInstance && pHiddInstance->ctrlCh.cid) 
    {
        if (!hiddConvInst)
        {
            hiddConvInst = CsrBtMarshalUtilCreate(CSR_BT_MARSHAL_UTIL_SERIALIZER);
        }
 
        CsrBtMarshalUtilResetBuffer(hiddConvInst, length, buf, TRUE);
        CsrBtMarshalUtilConvertObj(hiddConvInst, pHiddInstance->myAppHandle);
        serHiddMainInstanceData(hiddConvInst, pHiddInstance, &tpAddrt.addrt.addr);
        *written = length - CsrBtMarshalUtilRemainingLengthGet(hiddConvInst);
        return CsrBtMarshalUtilStatus(hiddConvInst);
    }
    else
    {
        if (pHiddInstance)
        {
            CSR_LOG_TEXT_INFO((CsrBtHiddLto, 0, "csrBtHiddMarshal:: Invalid ctrlCh.cid(%d), state(%d)", pHiddInstance->ctrlCh.cid, pHiddInstance->state));
        }
        return TRUE;
    }
}

static bool csrBtHiddUnmarshal(const tp_bdaddr *vmTpAddrt,
                               const CsrUint8 *buf,
                               CsrUint16 length,
                               CsrUint16 *written)
{
    HiddInstanceDataType *pHiddInstance = NULL;
    CsrBtTpdAddrT tpAddrt = { 0 };
    CsrSchedQid queueId = 0;
 
    CSR_LOG_TEXT_INFO((CsrBtHiddLto, 0, "csrBtHiddUnmarshal"));
    BdaddrConvertTpVmToBluestack(&tpAddrt, vmTpAddrt);
    if (!hiddConvInst)
    {
        hiddConvInst = CsrBtMarshalUtilCreate(CSR_BT_MARSHAL_UTIL_DESERIALIZER);
    }

    CsrBtMarshalUtilResetBuffer(hiddConvInst, length, (void *) buf, TRUE);
    CsrBtMarshalUtilConvertObj(hiddConvInst, queueId);
    pHiddInstance = hiddGetInstanceFromQueueId(queueId);
    PanicIfFalse(pHiddInstance != NULL);
    deserHiddMainInstanceData(hiddConvInst, pHiddInstance, &tpAddrt.addrt.addr);
    *written = length - CsrBtMarshalUtilRemainingLengthGet(hiddConvInst);
 
    return CsrBtMarshalUtilStatus(hiddConvInst);
}

static void csrBtHiddHandoverCommit(const tp_bdaddr *vmTpAddrt,
                                    bool newPrimary)
{
    CSR_LOG_TEXT_INFO((CsrBtHiddLto, 0, "csrBtHiddHandoverCommit"));
 
    if(newPrimary)
    {
        HiddInstanceDataType *pHiddInstance = NULL;
        CsrBtTpdAddrT tpAddrt = { 0 };

        BdaddrConvertTpVmToBluestack(&tpAddrt, vmTpAddrt);
        pHiddInstance = hiddGetInstanceFromBdAddr(&tpAddrt.addrt.addr);

        if (pHiddInstance)
        {
            if (pHiddInstance->ctrlCh.cid)
            {
                registerStreams(pHiddInstance->ctrlCh.cid, pHiddInstance->myAppHandle);
            }
            if (pHiddInstance->intrCh.cid)
            {
                registerStreams(pHiddInstance->intrCh.cid, pHiddInstance->myAppHandle);
            }
            if (pHiddInstance->ctrlCh.cid && pHiddInstance->intrCh.cid)
            {
                pHiddInstance->state = HIDD_CONNECTED_STATE;
            }
            else if (pHiddInstance->ctrlCh.cid && !(pHiddInstance->intrCh.cid))
            {   /* Remote HIDH has connected only HID control channel */
                pHiddInstance->state = HIDD_ACCEPT_CONNECTING_STATE;
            }
        }
    }
}

static void csrBtHiddHandoverComplete(bool newPrimary)
{
    CSR_UNUSED(newPrimary);
    CSR_LOG_TEXT_INFO((CsrBtHiddLto, 0, "csrBtHiddHandoverComplete"));
    if (hiddConvInst)
    {
        CsrBtMarshalUtilDestroy(hiddConvInst);
        hiddConvInst = NULL;
    }
}

static void csrBtHiddHandoverAbort(void)
{
    CSR_LOG_TEXT_INFO((CsrBtHiddLto, 0, "csrBtHiddHandoverAbort"));
    if (hiddConvInst && CsrBtMarshalUtilTypeGet(hiddConvInst) == CSR_BT_MARSHAL_UTIL_DESERIALIZER)
    {
#ifndef INSTALL_HIDD_MULTI_INSTANCE_SUPPORT
        HiddCleanUpAfterHandoverFailure(&csrBtHiddInstData);
#else
        {
            CsrUint8 instIndex;
            for (instIndex =0; instIndex < HIDD_MAX_NUM_INSTANCES; instIndex++)
            {
                HiddCleanUpAfterHandoverFailure(&csrBtHiddInstData[instIndex]);
            }
        }
#endif
    }
    csrBtHiddHandoverComplete(FALSE);
}

const handover_interface csr_bt_hidd_handover_if =
    MAKE_BREDR_HANDOVER_IF_VPL(
    &csrBtHiddVeto,
    &csrBtHiddVetoLink,
    &csrBtHiddMarshal,
    &csrBtHiddUnmarshal,
    &csrBtHiddHandoverCommit,
    &csrBtHiddHandoverComplete,
    &csrBtHiddHandoverAbort);

